#include "ArkTsObjectManager.h"
#include "aki/jsbind.h"
#include <chrono>
#include <thread>
namespace napiT {
std::map<std::string, Jsobject> jsobjectMap;
std::mutex jsobjectMapMutex;
napi_env g_jsEnv = nullptr;
uv_loop_t *g_jsLoop = nullptr;
} // namespace napiT

static void AsyncWorkHandler(uv_async_t *handle) {
    AKI_LOG(ERROR) << "fjx AsyncWorkHandler ok";
    napiT::AsyncWorkData *workData = static_cast<napiT::AsyncWorkData *>(handle->data);
    napi_handle_scope scope;
    
    if (napiT::g_jsEnv == nullptr) {
        AKI_LOG(ERROR) << "fjx g_jsEnv is null!";
        return;
    }
    
    napi_status status = napi_open_handle_scope(napiT::g_jsEnv, &scope);
    if (status != napi_ok) {
        AKI_LOG(ERROR) << "fjx Failed to open handle scope";
        return;
    }
    
    napi_ref resultRef = nullptr;
    {
        std::lock_guard<std::mutex> lock(napiT::jsobjectMapMutex);
        auto it = napiT::jsobjectMap.find(workData->prototypeName);
        if (it != napiT::jsobjectMap.end()) {
            napiT::Jsobject &prototype = it->second;
            napi_value tsObject;
            AKI_LOG(ERROR) << "fjx prototype ." << prototype.name;
            if (napi_get_reference_value(napiT::g_jsEnv, prototype.ref, &tsObject) != napi_ok) {
                AKI_LOG(ERROR) << "fjx Failed to get constructor";
                napi_close_handle_scope(napiT::g_jsEnv, scope);
                return;
            }

            const char *str = static_cast<const char *>(workData->params[0]);
            const char *str2 = static_cast<const char *>(workData->params[1]);
            int age = *static_cast<int *>(workData->params[2]);
            napi_value value = napiT::charValueToNapi(napiT::g_jsEnv, str);
            napi_value valu2 = napiT::charValueToNapi(napiT::g_jsEnv, str2);
            napi_value valu3 = napiT::intValueToNapi(napiT::g_jsEnv, age);
            napi_value argv[3] = {value, valu2, valu3};
            
            napi_value instance;
            status = napi_new_instance(napiT::g_jsEnv, tsObject, 3, argv, &instance);
            if (status == napi_ok) {
                AKI_LOG(ERROR) << "fjx napi_new_instance ok";
                if (napi_create_reference(napiT::g_jsEnv, instance, 1, &resultRef) != napi_ok) {
                    AKI_LOG(ERROR) << "fjx Failed to create instance reference";
                }
            } else {
                AKI_LOG(ERROR) << "fjx napi_new_instance fail";
            }
            
            if (workData->callback && resultRef != nullptr) {
                workData->callback(resultRef); // 将结果引用传递给回调
            }

            uv_close(reinterpret_cast<uv_handle_t *>(handle), [](uv_handle_t *h) {
                napiT::AsyncWorkData *data = static_cast<napiT::AsyncWorkData *>(h->data);
                AKI_LOG(INFO) << "fjx Cleaning up AsyncWorkData";

                for (void *ptr : data->params) {
                    if (ptr != nullptr) {
                        free(ptr); 
                    }
                }

                if (data->resultRef != nullptr) {
                    napi_delete_reference(napiT::g_jsEnv, data->resultRef);
                }
                delete data;
            });
            
            napi_close_handle_scope(napiT::g_jsEnv, scope);
        }
    }
}

napi_ref napiT::CreateObjectCrossThread(const std::string &key, const std::vector<void *> &params,
                                        std::function<void(napi_ref)> callback) {
    AsyncWorkData *workData = new AsyncWorkData();
    workData->prototypeName = key;
    workData->params = params;
    workData->callback = callback; // 绑定回调

    // 初始化 async 句柄（必须在 JS 线程）
    uv_async_init(napiT::g_jsLoop, &workData->async, AsyncWorkHandler);
    workData->async.data = workData;

    uv_async_send(&workData->async); // 发送异步任务
    return nullptr;                  // 结果通过回调返回
}

void napiT::dialog(napi_ref &value) {
    std::vector<void *> params = {
        strdup("樊佳鑫"), 
        strdup("男"),
        new int32_t(26)
    };
    
    CreateObjectCrossThread("Person", params, [&value](napi_ref personRef) {
        // 此回调在对象创建完成后触发（运行在 JS 线程）
        AsyncWorkData *methodWorkData = new AsyncWorkData();
        methodWorkData->resultRef = personRef;
        
//        value = personRef;

        // 初始化异步任务调用 sayHi 方法
        uv_async_init(napiT::g_jsLoop, &methodWorkData->async, [](uv_async_t *handle) {
            AsyncWorkData *data = static_cast<AsyncWorkData *>(handle->data);
            napi_handle_scope scope;
            napi_open_handle_scope(napiT::g_jsEnv, &scope);

            // 获取对象实例
            napi_value personObj;
            napi_get_reference_value(napiT::g_jsEnv, data->resultRef, &personObj);

            // 调用 sayHi 方法
            napi_value func;
            if (napi_get_named_property(napiT::g_jsEnv, personObj, "sayHi", &func) == napi_ok) {
                napi_value result;
                napi_call_function(napiT::g_jsEnv, personObj, func, 0, nullptr, &result);
            }

            // 清理资源
            napi_close_handle_scope(napiT::g_jsEnv, scope);
            uv_close((uv_handle_t *)handle, [](uv_handle_t *h) { delete static_cast<AsyncWorkData *>(h->data); });
        });

        methodWorkData->async.data = methodWorkData;
        uv_async_send(&methodWorkData->async); // 触发方法调用
    });
}

std::string napiT::napiValueToString(napi_env env, napi_value value) {
    size_t length = 0;
    napi_get_value_string_utf8(env, value, nullptr, 0, &length);
    std::vector<char> buffer(length + 1);
    napi_get_value_string_utf8(env, value, buffer.data(), buffer.size(), &length);
    return std::string(buffer.data(), length);
}

int napiT::napiValueToInt(napi_env env, napi_value value) {
    int32_t result;
    napi_status status = napi_get_value_int32(env, value, &result);
    if (status != napi_ok) {
        return 0;
    }
    return result;
}

napi_value napiT::charValueToNapi(napi_env env, const char *str) {
    size_t length = strlen(str);
    napi_value value = nullptr;
    napi_create_string_utf8(env, str, length, &value);
    return value;
}

napi_value napiT::intValueToNapi(napi_env env, int32_t num) {
    napi_value value = nullptr;
    napi_create_int32(env, num, &value);
    return value;
}
